package sk.upjs.ariadne.editor.maplayers;

import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;

import javax.imageio.ImageIO;
import javax.swing.*;

import sk.upjs.ariadne.editor.MapLayer;

/**
 * Map layer for displaying an image that can be used as an underlay.
 */
public class ImageLayer extends MapLayer {

	/**
	 * Image that is displayed in this layer.
	 */
	private BufferedImage layerImage;

	/**
	 * Position of the layer image (position of the image center).
	 */
	private Point2D imagePosition;

	/**
	 * Current zoom of the layer image.
	 */
	private double imageZoom = 1;

	/**
	 * Control panel for managing this map layer.
	 */
	private ImageLayerControlPanel controlPanel;

	/**
	 * Rotation of the layer image. The rotation is defined by an angle in
	 * degrees.
	 */
	private double imageRotation = 0;

	/**
	 * Image file that is displayed in this layer
	 */
	private File imageFile;

	/**
	 * Indicates whether a dragging is performed.
	 */
	private boolean dragging = false;

	/**
	 * Coordinates, where dragging started
	 */
	private Point2D dragStartPosition = null;

	/**
	 * Coordinates of previous mouse position
	 */
	private Point2D dragPreviousPreviousPos = null;

	/**
	 * Constructs a new image layer.
	 */
	public ImageLayer() {
		controlPanel = new ImageLayerControlPanel(this);
	}

	/**
	 * Load an image from a file.
	 * 
	 * @param imageFile
	 *            the image file whose content will be displayed in this panel.
	 * 
	 * @return true, if the image file was loaded, false otherwise.
	 */
	public boolean loadImage(File imageFile) {
		layerImage = null;
		imageZoom = 1;
		imageRotation = 0;
		try {
			layerImage = ImageIO.read(imageFile);
			this.imageFile = imageFile;
			imagePosition = new Point2D.Double(layerImage.getWidth() / 2,
					layerImage.getHeight() / 2);
		} catch (IOException e) {
			return false;
		}

		repaint();
		return true;
	}

	@Override
	public void paint(Graphics2D g, Rectangle2D area) {
		if (layerImage == null)
			return;

		g.translate(imagePosition.getX() * getParentZoom(),
				imagePosition.getY() * getParentZoom());

		if (imageRotation != 0)
			g.rotate(Math.toRadians(imageRotation));

		double zoomedWidth = layerImage.getWidth() * getParentZoom();
		double zoomedHeight = layerImage.getHeight() * getParentZoom();

		g.translate(-zoomedWidth / 2, -zoomedHeight / 2);
		g.drawImage(layerImage, 0, 0, (int) Math.round(zoomedWidth),
				(int) Math.round(zoomedHeight), null);
	}

	@Override
	public void paintEditOverlay(Graphics2D g, Rectangle2D area) {
		if (layerImage == null)
			return;

		g.translate(imagePosition.getX() * getParentZoom(),
				imagePosition.getY() * getParentZoom());

		if (imageRotation != 0)
			g.rotate(Math.toRadians(imageRotation));

		double zoomedWidth = layerImage.getWidth() * getParentZoom();
		double zoomedHeight = layerImage.getHeight() * getParentZoom();

		g.translate(-zoomedWidth / 2, -zoomedHeight / 2);

		g.setPaint(Color.black);
		g.setStroke(new BasicStroke(1f, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND, 1f, new float[] { 2f }, 0f));

		g.draw(new Rectangle2D.Double(0, 0, zoomedWidth, zoomedHeight));
	}

	@Override
	protected void onMousePressed(Point2D coordinates, MouseEvent e) {
		if (layerImage == null)
			return;

		if (SwingUtilities.isLeftMouseButton(e)) {
			dragging = true;
			dragStartPosition = coordinates;
			dragPreviousPreviousPos = coordinates;
		}
	}

	protected void onMouseDragged(Point2D coordinates, MouseEvent e) {
		if (dragging) {
			Point2D diff = new Point2D.Double(coordinates.getX()
					- dragPreviousPreviousPos.getX(), coordinates.getY()
					- dragPreviousPreviousPos.getY());
			Point2D startDiff = new Point2D.Double(coordinates.getX()
					- dragStartPosition.getX(), coordinates.getY()
					- dragStartPosition.getY());

			imagePosition.setLocation(imagePosition.getX() + diff.getX(),
					imagePosition.getY() + diff.getY());

			dragPreviousPreviousPos = coordinates;
			repaint();
		}
	}

	@Override
	protected void onMouseReleased(Point2D coordinates, MouseEvent e) {
		if (dragging && SwingUtilities.isLeftMouseButton(e)) {
			dragging = false;
			dragStartPosition = null;
			dragPreviousPreviousPos = null;
		}
	}

	@Override
	public Rectangle2D getBoundingRect() {
		if (layerImage == null)
			return null;

		// only a preliminary version that does not take into account rotation
		return new Rectangle2D.Double(imagePosition.getX()
				- layerImage.getWidth() * imageZoom / 2, imagePosition.getY()
				- layerImage.getHeight() * imageZoom / 2, layerImage.getWidth()
				* imageZoom, layerImage.getHeight() * imageZoom);
	}

	@Override
	public String getLayerName() {
		if (imageFile != null)
			return "Image layer (" + imageFile.getName() + ")";
		else
			return "Image layer (no image)";
	}

	@Override
	public JPanel getControlPanel() {
		return controlPanel;
	}
}
